在前一篇文章中,我們學習了如何整合多個 LLM 供應商,能夠靈活選擇最適合的模型 (供應商)。但到目前為止,我們的 Agent 只能進行對話,還無法執行具體的操作。今天我們將學習 Koog 框架的工具系統基礎,讓 AI Agent 具備實際的操作能力
在 AI Agent 的世界中,工具(Tool) 是連接 AI 思維與現實世界的橋樑。就像人類使用計算機、電話、筆記本來完成各種任務一樣,AI Agent 也需要工具來執行具體操作
Koog 的工具系統設計理念很簡單
讓 AI Agent 能夠安全且可控地執行實際操作
如果你有使用其他 AI 開發框架的經驗,你可能會發現 Koog 的「工具(Tool)」概念似曾相識。沒錯!Koog 的工具系統本質上就是 LLM Function Calling 的實作
在不同的語言和框架中,這個概念有著不同的名稱
框架/語言 | 名稱 | 描述 |
---|---|---|
Koog (Kotlin) | Tool | 使用 SimpleTool 或註解方式定義 |
OpenAI API | Function Calling | 直接定義 JSON Schema |
LangChain (Python) | Tool | 使用裝飾器或繼承 BaseTool |
AutoGen | Function Calling | 註冊 Python 函數 |
Semantic Kernel (C#) | Plugin/Function | 使用屬性標註方法 |
核心概念都相同:讓 LLM 能夠呼叫預先定義的函數來執行具體操作,並將結果整合到對話流程中
Koog 的優勢在於
Koog 框架提供了三個基本的內建工具,讓我們先了解這些工具的基本用法
SayToUser
工具讓 Agent 能夠主動向使用者輸出訊息
suspend fun main() {
// 註冊工具
val toolRegistry = ToolRegistry {
tool(SayToUser)
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
systemPrompt = "你是友善的 AI 助手,請使用 SayToUser 工具與使用者對話",
toolRegistry = toolRegistry,
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
agent.run("你好!")
}
Agent says: 你好!很高興見到你,有什麼我可以幫助你的嗎?
AskUser
工具讓 Agent 能夠主動詢問使用者問題
suspend fun main() {
// 註冊工具
val toolRegistry = ToolRegistry {
tool(SayToUser)
tool(AskUser)
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
// systemPrompt = "請使用 AskUser 詢問使用者的姓名,然後用 SayToUser 打招呼。",
systemPrompt = "請先詢問使用者的姓名,然後在跟使用者打招呼。",
toolRegistry = toolRegistry,
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
agent.run("你好!")
}
實測,有沒有在
system prompt
裡寫明使用工具
,得到的結果都是一樣的
您好!請問您的姓名是?
Cash
Agent says: 您好,Cash!很高興見到您,有什麼我可以幫助您的嗎?
ExitTool
工具讓 Agent 能夠優雅地結束對話
suspend fun main() {
// 註冊工具
val toolRegistry = ToolRegistry {
tool(SayToUser)
tool(AskUser)
tool(ExitTool)
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
// systemPrompt = "請使用 AskUser 詢問使用者的姓名,然後用 SayToUser 打招呼。",
systemPrompt = "請先詢問使用者的姓名,然後在跟使用者打招呼。",
toolRegistry = toolRegistry,
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
agent.run("你好!")
}
你好!請問您的姓名是?
> Cash
Agent says: Cash,您好!很高興認識您。請問有什麼我可以幫助您的嗎?
請問您今天想聊些什麼,或者有什麼需要幫忙的?
> 沒有
內建工具雖然實用,但真正的威力在於開發自定義工具。Koog 提供了 SimpleTool
抽象類,讓我們能夠輕鬆建立自己的工具
每個自定義工具都需要四個基本要素
先確認 build.gradle.kts
中是否已加入 serializtion
plugin
plugins {
kotlin("jvm") version "2.2.0"
// 確認有加上相關的序列化插件
kotlin("plugin.serialization") version "2.2.0"
}
object AddTool : SimpleTool<AddTool.Args>() {
// 1. 定義參數類別
@Serializable
data class Args(val number1: Int, val number2: Int) : ToolArgs
// 2. 設定序列化器
override val argsSerializer = Args.serializer()
// 3. 定義工具描述
override val descriptor = ToolDescriptor(
name = "add_numbers",
description = "將兩個數字相加",
requiredParameters = listOf(
ToolParameterDescriptor(
name = "number1",
description = "第一個數字",
type = ToolParameterType.Integer
),
ToolParameterDescriptor(
name = "number2",
description = "第二個數字",
type = ToolParameterType.Integer
)
)
)
// 4. 實作執行邏輯
override suspend fun doExecute(args: Args): String {
return try {
val result = args.number1 + args.number2
"計算結果:${args.number1} + ${args.number2} = $result"
} catch (e: Exception) {
"計算錯誤:${e.message}"
}
}
}
suspend fun main() {
// 註冊工具
val toolRegistry = ToolRegistry {
tool(SayToUser)
tool(AddTool)
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
systemPrompt = """
你是一個數學助手。當使用者需要計算兩個數字相加時:
1. 使用 add_numbers 工具進行計算
2. 使用 SayToUser 工具告訴使用者結果
請用友善的正體中文回應
""".trimIndent(),
toolRegistry = toolRegistry,
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
// 測試加法功能
agent.run("請幫我計算 15 + 27")
}
執行這個程式時,AI Agent 會
doExecute
來計算結果我覺得這裡和其它語言的 tool 方式不太一樣,不用明確的實作工具的方法,而是實作
doExecute
,所以程式碼裡面可以沒有addNumbers
函數
SayToUser
工具告訴使用者答案是 42Agent says: 15 加 27 的結果是 42。
除了 Class-based 的方式,Koog 還提供了更簡潔的 Annotation-based 工具開發方式。這種方法使用註解來自動生成工具描述,讓開發者能夠以更宣告式的方式建立工具
Annotation-based 工具提供了一種宣告式的方式來將函數公開為 LLM 可用的工具。透過使用註解,我們可以將現有的函數轉換為 LLM 可使用的工具,而無需手動實作工具描述
@Tool
:標記函數為可供 LLM 使用的工具@LLMDescription
:為工具、參數提供描述資訊首先,我們需要建立一個繼承 ToolSet
的類別
@LLMDescription("數學計算工具集")
class MathToolSet : ToolSet {
@Tool
@LLMDescription("將兩個數字相加")
fun addNumbers(
@LLMDescription("第一個數字")
number1: Int,
@LLMDescription("第二個數字")
number2: Int
): String {
return try {
val result = number1 + number2
"計算結果:$number1 + $number2 = $result"
} catch (e: Exception) {
"計算錯誤:${e.message}"
}
}
}
suspend fun main() {
// 註冊工具集
val toolRegistry = ToolRegistry {
tool(SayToUser)
// 使用 tools 方法註冊
tools(MathToolSet())
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
systemPrompt = """
你是一個數學助手。當使用者需要計算兩個數字相加時:
1. 使用 addNumbers 工具進行計算
2. 使用 SayToUser 工具告訴使用者結果
請用友善的正體中文回應
""".trimIndent(),
toolRegistry = toolRegistry,
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
// 測試加法功能
agent.run("請幫我計算 25 + 17")
}
Agent says: 25 加 17 的結果是 42。
特性 | Class-based | Annotation-based |
---|---|---|
語法複雜度 | 較複雜,需要實作多個部分 | 較簡潔,使用註解即可 |
參數定義 | 需要定義 Args 類別和序列化器 | 直接在函數參數上使用註解 |
工具描述 | 手動定義 ToolDescriptor | 自動從註解生成 |
適用場景 | 複雜工具,需要精細控制 | 簡單工具,快速開發 |
一個 ToolSet 可以包含多個工具
@LLMDescription("數學計算工具集")
class MathToolSet : ToolSet {
@Tool
@LLMDescription("將兩個數字相加")
fun addNumbers(
@LLMDescription("第一個數字")
number1: Int,
@LLMDescription("第二個數字")
number2: Int
): String {
return try {
val result = number1 + number2
"計算結果:$number1 + $number2 = $result"
} catch (e: Exception) {
"計算錯誤:${e.message}"
}
}
@Tool
@LLMDescription("將兩個數字相乘")
fun multiplyNumbers(
@LLMDescription("第一個數字")
number1: Int,
@LLMDescription("第二個數字")
number2: Int
): String {
return try {
val result = number1 * number2
"乘法結果:$number1 × $number2 = $result"
} catch (e: Exception) {
"計算錯誤:${e.message}"
}
}
@Tool
@LLMDescription("檢查數字是否為質數")
fun isPrime(
@LLMDescription("要檢查的數字")
number: Int
): String {
if (number <= 1) return "$number 不是質數"
for (i in 2..sqrt(number.toDouble()).toInt()) {
if (number % i == 0) return "$number 不是質數"
}
return "$number 是質數"
}
}
suspend fun main() {
// 註冊工具集
val toolRegistry = ToolRegistry {
tool(SayToUser)
// 使用 tools 方法註冊
tools(MathToolSet())
}
val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
systemPrompt = """
你是一個數學助手。你有一些數學工具可以使用
請用友善的正體中文回應
""".trimIndent(),
// systemPrompt = """
// 你是一個數學助手。你有一些數學工具可以使用:
// 1. 將兩個數字相加 - addNumbers
// 2. 將兩個數字相乘 - multiplyNumbers
// 3. 檢查數字是否為質數 - isPrime
// 當使用者有相關的數學問題時,請選擇相對應的工具來處理和回應
// 請用友善的正體中文回應
// """.trimIndent(),
toolRegistry = toolRegistry,
// llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
llmModel = OpenAIModels.CostOptimized.GPT4_1
)
// 測試加法功能
agent.run("請幫我計算 25 + 17")
// 測試乘法功能
agent.run("請幫我計算 4 * 5")
// 測試質數功能
agent.run("請問一下 5 是不是質數")
}
這裡我試了 4.1 mini 好幾次,都沒有辦法順利執行,就算是把工具描述的很清楚也是一樣,最後改用 4.1 就可以成功執行
如果有遇到工具沒有辦法順序執行的,可以嘗試換一個 (更聰明的) 模型試試看
Agent says: 25 + 17 的答案是 42。
Agent says: 4 × 5 = 20
Agent says: 5 是質數,因為它只有 1 和自己可以整除。
使用 Class-based
使用 Annotation-based
每個工具都應該專注於一個明確的任務
// ✅ 好的設計:功能明確
object AddTool : SimpleTool<AddTool.Args>() {
// 只做加法運算
}
// ❌ 避免的設計:功能過於複雜
object MathTool : SimpleTool<MathTool.Args>() {
// 同時處理加減乘除、三角函數、統計等
}
讓 LLM 能夠正確理解如何使用工具
ToolParameterDescriptor(
name = "number1",
description = "第一個數字", // 清楚說明參數用途
type = ToolParameterType.Integer
)
提供有意義的錯誤訊息
override suspend fun doExecute(args: Args): String {
return try {
// 執行工具邏輯
performOperation(args)
} catch (e: Exception) {
"操作失敗:${e.message}"
}
}
今天我們學習了 Koog 工具系統的基礎概念
下一篇文章中,我們將學習如何調校 AI Agent 的行為參數,讓它更符合我們的需求
圖片來源:AI 產生